﻿using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;

namespace AnjLab.SyncIT.Core
{
    public class CryptographyTool
    {
        public static readonly string KeyConstant = GetKeyConstant();
        public static readonly string IVConstant = Reverse(KeyConstant);

        private static string Reverse(IEnumerable<char> s)
        {
            var sb = new StringBuilder();

            foreach (var c in s)
            {
                sb.Insert(0, c);
            }

            return sb.ToString();
        }

        private static string GetKeyConstant()
        {
            string sid = "";

            try
            {
                sid = UserPrincipal.Current.Sid.Value;

                Library.Log.Debug("Got SID from UserPrincipal");
            } 
            catch (Exception e)
            {
                Library.Log.Warning("Error getting SID from UserPrincipal: {0}", e);

                Library.Log.Debug("Using WindowsIdentity to get SID");

                var currentIdentity = WindowsIdentity.GetCurrent();
                if (currentIdentity != null)
                {
                    Library.Log.Debug("Got SID from WindowsIdentity");

                    sid = currentIdentity.User.Value;
                }
                else
                {
                    Library.Log.Info("Couldn't get the SID, using default one");

                    sid = "S-0-0-00-1234567890-1234567890-1234567890-0000";
                }
            }

            string sidPart = sid.Substring(8, sid.Length - 8).Replace("-", "");
            
            sidPart = sidPart.Length < 16 ? sidPart + RepeatChar('8', 16 - sidPart.Length) : sidPart.Substring(0, 16);

            return sidPart;
        }

        private static string RepeatChar(char c, int count)
        {
            var sb = new StringBuilder();
            
            for (int i = 0; i < count; i++)
            {
                sb.Append(c);
            }

            return sb.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encryptedText">Encrypted text in base64</param>
        /// <returns></returns>
        public static string Decrypt(string encryptedText)
        {
            var textConverter = new UTF8Encoding();
            var myRijndael = new RijndaelManaged();

            var key = textConverter.GetBytes(KeyConstant);
            var IV = textConverter.GetBytes(IVConstant);

            //Get a decryptor that uses the same key and IV as the encryptor.
            var decryptor = myRijndael.CreateDecryptor(key, IV);

            var encrypted = Convert.FromBase64String(encryptedText);

            //Now decrypt the previously encrypted message using the decryptor
            // obtained in the above step.
            var msDecrypt = new MemoryStream(encrypted);
            var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

            var fromEncrypt = new byte[encrypted.Length];

            //Read the data out of the crypto stream.
            int bytesRead = csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);

            //Convert the byte array back into a string.
            return textConverter.GetString(fromEncrypt, 0, bytesRead);
        }

        public static string Encrypt(string plainText)
        {
            var textConverter = new UTF8Encoding();
            var myRijndael = new RijndaelManaged();

            var key = textConverter.GetBytes(KeyConstant);
            var IV = textConverter.GetBytes(IVConstant);

            //Get an encryptor.
            var encryptor = myRijndael.CreateEncryptor(key, IV);

            //Encrypt the data.
            var msEncrypt = new MemoryStream();
            var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);

            //Convert the data to a byte array.
            var toEncrypt = textConverter.GetBytes(plainText);

            //Write all data to the crypto stream and flush it.
            csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
            csEncrypt.FlushFinalBlock();

            //Get encrypted array of bytes.
            var encrypted = msEncrypt.ToArray();

            return Convert.ToBase64String(encrypted);
        }
    }
}
